<script setup>
import {
    reactive,
    ref,
    onMounted,
    getCurrentInstance,
    nextTick,
    watch,
    computed, onUnmounted
} from "vue";
import { useRoute, useRouter } from "vue-router";
import { showLoadingToast, showToast, closeToast, showSuccessToast } from "vant";
import Recorder from "recorder-core"; // 导入Recorder核心
import "recorder-core/src/engine/mp3"; // 导入mp3编码器
import "recorder-core/src/engine/mp3-engine"; // 导入mp3引擎
import { marked } from "marked";
import sanitizeHtml from "sanitize-html";
import huatong from "@/assets/img/huatong.png";
import { useChatStore } from "@/store/index";

const store = useChatStore();
const {
    proxy: { $http },
} = getCurrentInstance();
const route = useRoute();
const router = useRouter();

const props = defineProps({
    socketData: {
        type: Object,
        default: null,
    },
    loading: {
        type: Object,
        default: null,
    },
});

// 使用computed获取当前路由名称
const currentRouteName = computed(() => route.name);

const dataVal = reactive({
    huatong,
    isVoiceMode: false,
    recording: false,
    inputValue: "",
    showMedia: false,
    recordingTimer: null,
    formData: {},
    isRecordingSupported: true,
    repairIsShutdownList: [
        { key: "是", value: 0 },
        { key: "否", value: 1 },
    ],
    repairFaultTypeList: [],
    repairFaultRatingList: [
        { text: "低", value: 0 },
        { text: "中", value: 1 },
        { text: "高", value: 2 },
        { text: "紧急", value: 3 },
    ],
    messages: [],
    scrollTop: 0,
    showForm: false,
    recorder: null, // Recorder实例
    showRecordingPopup: false,
    isCanceling: false,
    touchStartY: 0,
    recordingDuration: 0,
    showKeyboardIcon: true,
    volumeLevel: 0, // 音量级别
    isAIResponding: false, // 新增：AI是否正在回答
    currentResponse: "", // 新增：当前AI回答内容
    abortController: null, // 新增：用于终止请求的AbortController
    conversationId: null, // 确保conversationId在dataVal中定义
    // 修复：移除 pathName: currentRouteName，这会导致代理错误
});

// 配置 marked
marked.setOptions({
    breaks: true, // 转换换行符为 <br>
    gfm: true, // 启用 GitHub Flavored Markdown
});

const emits = defineEmits(["send", "add", "command"]);

// 安全地渲染 Markdown
const renderMarkdown = (markdown) => {
    if (!markdown) return "";
    // 先转换为 HTML
    const html = marked(markdown);

    // 然后清理 HTML 防止 XSS
    return sanitizeHtml(html, {
        allowedTags: sanitizeHtml.defaults.allowedTags.concat([
            "img",
            "h1",
            "h2",
            "h3",
            "h4",
        ]),
        allowedAttributes: {
            ...sanitizeHtml.defaults.allowedAttributes,
            a: ["href", "name", "target", "rel"],
            img: ["src", "alt", "title"],
        },
        allowedSchemes: ["http", "https", "mailto", "data"],
        transformTags: {
            a: (tagName, attribs) => {
                // 确保外部链接有 rel="noopener noreferrer"
                if (attribs.href && attribs.href.startsWith("http")) {
                    return {
                        tagName: "a",
                        attribs: {
                            ...attribs,
                            target: "_blank",
                            rel: "noopener noreferrer",
                        },
                    };
                }
                return { tagName, attribs };
            },
        },
    });
};

// 检查浏览器是否支持录音
const checkRecordingSupport = async () => {
    try {
        // 1. 检查基础API支持
        dataVal.isRecordingSupported = !!(
            navigator.mediaDevices &&
            navigator.mediaDevices.getUserMedia &&
            (window.AudioContext || window.webkitAudioContext)
        );

        if (!dataVal.isRecordingSupported) {
            showToast("浏览器不支持录音基础API");
            return false;
        }

        // 2. 检查Recorder核心库是否加载成功
        if (typeof Recorder === "undefined") {
            showToast("录音核心库加载失败");
            return false;
        }

        // 3. 检查麦克风设备是否存在
        const devices = await navigator.mediaDevices.enumerateDevices();
        const hasMicrophone = devices.some(
            (device) => device.kind === "audioinput"
        );
        if (!hasMicrophone) {
            showToast("未检测到麦克风设备");
            return false;
        }

        // 4. 尝试获取麦克风权限
        const stream = await navigator.mediaDevices.getUserMedia({
            audio: true,
        });
        stream.getTracks().forEach((track) => track.stop());

        return true;
    } catch (error) {
        console.error("录音支持检查失败:", error);
        let errorMsg = "无法访问麦克风";
        if (error.name === "NotAllowedError") {
            errorMsg = "麦克风权限被拒绝";
        } else if (error.name === "NotFoundError") {
            errorMsg = "未找到麦克风设备";
        }
        showToast(errorMsg);
        return false;
    }
};

// 初始化录音器
const initRecorder = async () => {
    try {
        // 正确的Recorder初始化方式
        dataVal.recorder = new Recorder({
            type: "mp3",
            sampleRate: 16000,
            bitRate: 16,
            onProcess: (buffers, powerLevel) => {
                dataVal.volumeLevel = Math.floor(powerLevel * 100);
            },
        });

        // 注意：这里使用标准的getUserMedia获取流
        const stream = await navigator.mediaDevices.getUserMedia({
            audio: true,
        });

        // 打开录音器
        await new Promise((resolve, reject) => {
            dataVal.recorder.open(
                () => resolve(), // 成功回调
                (error) => reject(error) // 失败回调
            );
        });

        return true;
    } catch (error) {
        console.error("录音初始化失败:", error);
        showToast("录音初始化失败: " + error.message);
        return false;
    }
};

// 开始录音
const startRecording = async (event) => {
    if (!dataVal.isRecordingSupported) return;

    try {
        // 1. 初始化录音器
        if (!dataVal.recorder) {
            const initSuccess = await initRecorder();
            if (!initSuccess) return;
        }

        // 2. 开始录音
        dataVal.recorder.start();
        dataVal.recording = true;
        dataVal.showRecordingPopup = true;
        dataVal.isCanceling = false;
        dataVal.recordingDuration = 0;

        // 3. 设置计时器
        dataVal.recordingTimer = setInterval(() => {
            dataVal.recordingDuration++;
            if (dataVal.recordingDuration >= 60) {
                stopRecording();
            }
        }, 1000);
    } catch (error) {
        console.error("开始录音失败:", error);
        showToast("开始录音失败: " + error.message);
    }
};

// 处理触摸移动（用于上滑取消）
const handleTouchMove = (event) => {
    if (!dataVal.recording) return;

    const touchY = event.touches[0].clientY;
    const deltaY = dataVal.touchStartY - touchY;

    if (deltaY > 50) {
        // 上滑超过50px视为取消
        dataVal.isCanceling = true;
    } else if (deltaY < 30) {
        dataVal.isCanceling = false;
    }
};

// 停止录音
const stopRecording = () => {
    if (!dataVal.recording) return;
    // 清除计时器
    clearInterval(dataVal.recordingTimer);
    dataVal.recording = false;
    dataVal.showRecordingPopup = false;

    // 移除事件监听
    document.removeEventListener("touchmove", handleTouchMove);

    // 正确的状态检查方式
    if (dataVal.recorder) {
        dataVal.recorder.stop(
            async (blob, duration) => {
                // 如果用户取消发送，则不处理录音数据
                if (dataVal.isCanceling) {
                    showToast("已取消录音");
                    return;
                }
                // 发送录音到后端
                await sendAudioToBackend(blob);

                // 关闭录音器释放资源
                dataVal.recorder.close();
                dataVal.recorder = null;
            },
            (error) => {
                console.error("停止录音失败:", error);
                showToast("停止录音失败");
            }
        );
    }
};

// 发送录音到后端
const sendAudioToBackend = async (audioBlob) => {
    dataVal.isVoice = true;
    if (dataVal.isCanceling || !audioBlob || audioBlob.size === 0) return;
    try {
        showLoadingToast({
            message: "正在处理中...",
            duration: 0,
        });
        const response = await $http.uploadAudio("/audio-to-text", audioBlob);

        if (response.text.length > 0) {
            sendMessage(response.text);
            // 添加用户消息
            addMessage(response.text, true, true, true);
            closeToast();
        } else {
            showToast("录音失败，请重试");
            setTimeout(() => {
                closeToast();
            }, 2000);
        }
    } catch (error) {
        console.error("语音上传失败:", error);
        showToast("语音上传失败");
    }
};

// 创建一个ref来存储取消函数
const cancelTokenSource = ref(null);

// 终止AI回答
const stopAIResponse = () => {
    if (dataVal.abortController) {
        dataVal.abortController.abort();
        dataVal.abortController = null;
        $http.cancelRequest(cancelTokenSource.value, "用户手动终止");
    }
    dataVal.isAIResponding = false;
    // 将当前回答内容保存为完整消息
    if (dataVal.currentResponse) {
        addMessage(dataVal.currentResponse, false, false);
        dataVal.currentResponse = "";
    }
};
// 添加指令处理状态跟踪
const commandProcessing = reactive({
    isProcessing: false,
    processedCommands: new Set(), // 用于跟踪已处理的指令ID
    currentCommandId: null
});
// 生成唯一的指令ID
const generateCommandId = (commandData) => {
    return JSON.stringify(commandData) + Date.now();
};

// 检查指令是否已处理
const isCommandProcessed = (commandId) => {
    return commandProcessing.processedCommands.has(commandId);
};

// 标记指令为已处理
const markCommandAsProcessed = (commandId) => {
    commandProcessing.processedCommands.add(commandId);

    // 清理过期的指令记录（防止内存泄漏）
    if (commandProcessing.processedCommands.size > 100) {
        const oldestKey = Array.from(commandProcessing.processedCommands)[0];
        commandProcessing.processedCommands.delete(oldestKey);
    }
};
// 处理指令消息
const handleCommand = async (commandText, commandSource = 'ai') => {
    // 生成指令ID用于去重
    const commandId = generateCommandId({ text: commandText, source: commandSource });

    // 检查是否已经处理过这个指令
    if (isCommandProcessed(commandId)) {
        console.log('指令已处理，跳过重复执行');
        return true;
    }

    // 标记为正在处理
    if (commandProcessing.isProcessing) {
        console.log('已有指令正在处理，等待完成');
        // 可以在这里实现指令队列，或者直接返回
        return false;
    }

    commandProcessing.isProcessing = true;
    commandProcessing.currentCommandId = commandId;

    try {
        // 提取cmd:后面的JSON部分
        const jsonStartIndex = commandText.indexOf('{');
        if (jsonStartIndex !== -1) {
            const jsonStr = commandText.substring(jsonStartIndex);

            try {
                // 尝试解析JSON部分
                const data = JSON.parse(jsonStr);

                // 标记指令为已处理
                markCommandAsProcessed(commandId);

                // 触发command事件，让父组件或路由处理
                emits("command", {
                    command: data,
                    response: null,
                    source: commandSource
                });

                // 根据指令类型执行不同的操作
                if (data.action) {
                    await executeLocalCommand(data);
                } else {
                    // 发送指令到对应接口
                    const query = {
                        from: store.platform,
                        ...data
                    };

                    const response = await $http.post('/jcsz/fromAndTo/send', query);

                    // 更新command事件
                    // emits("command", {
                    //     command: { ...data, isSent: false },
                    //     response: response.data,
                    //     source: commandSource
                    // });
                    addMessage('该指令已发送，请前往对应端查看', false, false);
                    // 显示成功消息
                    // showSuccessToast("指令执行成功");

                    // 如果有返回内容，添加到消息列表
                    // if (response.data && response.data.data && response.data.data.content) {
                    //     addMessage(response.data.data.content, false, false);
                    // }
                }

                return true;
            } catch (e) {
                console.error("解析指令JSON失败:", e);
                showToast("指令格式错误");
                return false;
            }
        } else {
            // 如果没有找到JSON部分
            showToast("无效的指令格式");
            return false;
        }
    } catch (error) {
        console.error("执行指令失败:", error);
        showToast("指令执行失败");
        return false;
    } finally {
        commandProcessing.isProcessing = false;
        commandProcessing.currentCommandId = null;
    }
};

// 执行本地指令（不需要发送到后端）
const executeLocalCommand = async (commandData) => {
    const { action, ...params } = commandData;

    switch (action) {
        case 'navigate':
            // 本地页面跳转
            if (params.route) {
                router.push(params.route);
                showSuccessToast(`跳转到${params.route}`);
            }
            break;

        case 'show_message':
            // 显示本地消息
            if (params.message) {
                addMessage(params.message, false, false);
                showSuccessToast("显示消息");
            }
            break;

        case 'clear_chat':
            // 清空聊天记录
            dataVal.messages = [];
            showSuccessToast("聊天已清空");
            break;

        case 'change_mode':
            // 切换模式
            if (params.mode === 'voice') {
                dataVal.isVoiceMode = true;
                showSuccessToast("切换到语音模式");
            } else if (params.mode === 'text') {
                dataVal.isVoiceMode = false;
                showSuccessToast("切换到文本模式");
            }
            break;

        default:
            // 未知的本地指令，发送到后端
            const query = {
                from: store.platform,
                ...commandData
            };

            const response = await $http.post('/jcsz/fromAndTo/send', query);

            // 如果有返回内容，添加到消息列表
            if (response.data && response.data.data && response.data.data.content) {
                addMessage(response.data.data.content, false, false);
            }
            break;
    }
};


// 发送消息给AI接口
const sendMessage = async (text) => {
    const item = {
        student:sessionStorage.getItem('studentNumber'),
        text
        // inputs: {},
        // query: text,
        // conversationId: dataVal.conversationId,
        // response_mode: "blocking",
        // user: store.userInfo?.userId.toString() ?? "abc-123",
        // files: [],
        // aimodelToken: store.modelToken,
    };

    // 创建AbortController用于终止请求
    dataVal.abortController = new AbortController();
    dataVal.isAIResponding = true;
    dataVal.currentResponse = "";

    try {
        const res = await $http.post("/student/generateImages", item, {
            signal: dataVal.abortController.signal,
        });

        if (res && res.data) {
            const item = JSON.parse(res.data.data);

            // 只有在 conversationId 为空时才更新它
            if (!dataVal.conversationId) {
                dataVal.conversationId = item.conversation_id;
            }

            // 检查是否为指令在半身屏展示张三的出行记录
            if (item.answer.startsWith("cmd")) {
                // 处理指令
                await handleCommand(item.answer);
            } else {
                // 普通消息
                addMessage(item.answer, false, false);
            }
        } else {
            addMessage("抱歉，我没有理解您的问题，请换个方式提问。", false, false);
        }
    } catch (error) {
        if (error.name !== "AbortError") {
            console.info("请求失败: " + error.message);
            addMessage("网络请求失败，请稍后重试。", false, false);
        }
    } finally {
        dataVal.isAIResponding = false;
        dataVal.abortController = null;
        dataVal.currentResponse = "";
        dataVal.inputValue = "";
    }
};

const clickText = (text) => {
    dataVal.inputValue = text;
    sendText();
};

// 发送文本消息
const sendText = (text) => {
    const item = text ?? dataVal.inputValue;
    if (!item.trim()) return;
    sendMessage(item);
    addMessage(item, true, false);
};

// 切换语音/文字模式
const toggleMode = () => {
    dataVal.isVoiceMode = !dataVal.isVoiceMode;
};

// 添加消息到聊天窗口
const addMessage = (text, isSent, isVoice = false) => {
    if (text) {
        const processedText = isSent ? text : renderMarkdown(text);
        const message = {
            text: processedText,
            isSent,
            time: new Date().toLocaleTimeString([], {
                hour: "2-digit",
                minute: "2-digit",
            }),
            isVoice,
            showFrom: !isSent && text.includes("表单"),
            rawText: text, // 保留原始文本以备后用
        };
        dataVal.messages.push(message);


    }
    emits("add", dataVal.messages || null, isSent, false);
};

// 修复：移除错误的赋值语句
// dataVal.pathName = computed(() => 'index'); // 这行会导致代理错误

// 监听socket数据和路由变化
watch(
    [() => props.socketData],
    ([newVal]) => {
        if (newVal?.type === "request") {
            sendText(newVal.data);
        } else if (newVal?.type === "show") {
            addMessage(newVal?.data, false, false);
        } else if (newVal?.type === "command") {
            // 处理来自socket的指令
            handleCommand(newVal.data);
        }
    },
    {
        immediate: true,
    }
);

defineExpose({
    addMessage,
    sendText,
    clickText,
    toggleMode,
    handleCommand, // 暴露指令处理方法
});

onMounted(async () => {
    await checkRecordingSupport();

    // 添加触摸事件监听
    document.addEventListener("touchmove", handleTouchMove);
});

// 组件卸载时清理资源
onUnmounted(() => {
    if (dataVal.recorder) {
        dataVal.recorder.close();
    }
    if (dataVal.abortController) {
        dataVal.abortController.abort();
    }
    document.removeEventListener("touchmove", handleTouchMove);
});
</script>

<template>
    <div class="ai-bottom">
        <div class="aibox d-flex align-items-center">
            <!-- 语音模式 -->
            <div class="voice-container">
                <van-icon :name="dataVal.jianpan" size="26" class="mode-switch-icon" @click="toggleMode" />
                <div class="voice-btn" @touchstart="startRecording" @touchend="stopRecording"
                    @touchcancel="stopRecording" :class="{ canceling: dataVal.isCanceling }">
                    {{
                        dataVal.recording
                            ? dataVal.isCanceling
                                ? "松开取消"
                                : "松开结束"
                            : "按住说话"
                    }}
                </div>
            </div>

            <!-- 录音提示弹窗 -->
            <van-popup v-model:show="dataVal.showRecordingPopup" round position="center"
                :style="{ height: '30%', width: '80%' }">
                <div class="recording-popup">
                    <div class="volume-indicator">
                        <div v-for="i in 5" :key="i" class="volume-bar" :class="{
                            active: dataVal.volumeLevel > (i - 1) * 20,
                        }" :style="{ height: `${i * 15}px` }"></div>
                    </div>

                    <div class="recording-status d-flex align-items-center">
                        <van-icon name="delete" size="24" v-if="dataVal.isCanceling" />
                        <van-icon name="volume-o" size="24" v-else />
                        <div class="status-text ml-2">
                            {{
                                dataVal.isCanceling
                                    ? "上滑取消录音"
                                    : "录音中..."
                            }}
                        </div>
                    </div>

                    <div class="recording-timer">
                        {{ dataVal.recordingDuration }}秒
                    </div>

                    <div class="recording-tip">最长可录音60秒</div>
                </div>
            </van-popup>
        </div>
        <!-- <div class="tips mt-2 text-c">内容由AI生成，仅供参考</div> -->
        <!-- 上传弹窗 -->
        <van-popup v-model:show="dataVal.showUploadPopup" round closeable position="bottom" :style="{ height: '50%' }">
            <div class="d-flex direction-column p-3 showUploadPopup">
                <h3>上传文件</h3>
                <div class="text-tips-gray-text">
                    上传文件后，可以根据文件中的信息进行深入解读和扩展回答。支持多种格式：PDF、DOC、JPEG/PNG图片等（文件大小不超过20MB）
                </div>
                <div class="d-flex space-around mt-3">
                    <div class="d-flex direction-column align-items-center justify-content-center btnbox">
                        <div class="imagebox d-flex align-items-center justify-content-center mb-1">
                            <van-icon name="photo-o" size="22" class="text-white" />
                        </div>
                        相册
                    </div>
                    <div class="d-flex direction-column align-items-center justify-content-center btnbox">
                        <div class="imagebox document d-flex align-items-center justify-content-center mb-1">
                            <van-icon name="todo-list-o" size="22" class="text-white" />
                        </div>
                        本地文件夹
                    </div>
                    <div class="d-flex direction-column align-items-center justify-content-center btnbox">
                        <div class="imagebox wechart d-flex align-items-center justify-content-center mb-1">
                            <van-icon :name="dataVal.iconWechat" size="30" class="text-white" />
                        </div>
                        微信文件
                    </div>
                </div>
                <h4>更多</h4>
                <div class="d-flex space-around align-items-center gap-1 flex-wrap wdith100">
                    <div class="d-flex justify-content-center align-items-center morebox">
                        <van-icon :name="dataVal.iconPdf" size="30" class="text-white mr-2" /><span
                            class="width50">PDF文档分析</span>
                    </div>
                    <div class="d-flex justify-content-center align-items-center morebox">
                        <van-icon :name="dataVal.iconPdf" size="30" class="text-white mr-2" /><span
                            class="width50">网页分析</span>
                    </div>
                    <div class="d-flex justify-content-center align-items-center morebox">
                        <van-icon :name="dataVal.iconAudio" size="30" class="text-white mr-2" /><span
                            class="width50">录音分析</span>
                    </div>
                    <div class="d-flex justify-content-center align-items-center morebox">
                        <van-icon :name="dataVal.iconVideo" size="30" class="text-white mr-2" />
                        <span class="width50">视频分析</span>
                    </div>
                </div>
            </div>
        </van-popup>
    </div>
</template>

<style scoped lang="scss">
.ai-bottom {
    width: 100%;
    box-sizing: border-box;
    padding: 0 20px;

    .aibox {
        background-color: #fff;
        padding: 0 6px;
        border-radius: 20px;
        box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
    }

    :deep(.van-cell) {
        display: flex;
        align-items: center;
        background: none;
        padding: 8px 10px;
    }

    .tips {
        color: #999;
    }

    .showUploadPopup {
        height: 100%;
        box-sizing: border-box;
        background-image: linear-gradient(to bottom, #e8ecff 20%, #ffffff 60%);

        .btnbox {
            width: 28%;
            padding: 16px 0;
            box-sizing: border-box;
            border-radius: 16px;
            background-color: #fff;
            box-shadow: 0 3px 6px rgba(187, 187, 187, 0.5);
        }

        .imagebox {
            width: 40px;
            height: 40px;
            background-color: #3684df;
            border-radius: 6px;

            &.document {
                background-color: #ff7c00;
            }

            &.wechart {
                background-color: #31bf58;
            }
        }

        .morebox {
            width: 45%;
            padding: 8px 0;
            background: #f3f4fa;
            pointer-events: auto;
            border-radius: 16px;
            color: #272d42;
        }
    }
}

.voice-container {
    display: flex;
    align-items: center;
    width: calc(100% - 40px);
    height: 50px;
    padding: 0 10px;

    .mode-switch-icon {
        margin-right: 15px;
    }

    .voice-btn {
        flex: 1;
        height: 40px;
        margin: 0 10px;
        background: #f0f0f0;
        border-radius: 25px;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 16px;
        color: #333;
        font-weight: 500;
        transition: all 0.3s;

        &:active {
            background-color: #e0e0e0;
        }

        &.canceling {
            background-color: #ffeceb;
            color: #ff4d4f;
        }
    }
}

.right-icons {
    width: 80px;
}

/* 录音弹窗样式 */
.recording-popup {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 100%;

    .volume-indicator {
        display: flex;
        align-items: flex-end;
        justify-content: center;
        gap: 8px;
        height: 60px;
        margin-bottom: 20px;

        .volume-bar {
            width: 8px;
            background-color: #e0e0e0;
            border-radius: 4px;
            transition: background-color 0.2s;

            &.active {
                background-color: #07c160;
            }
        }
    }

    .recording-status {
        .status-text {
            font-size: 16px;
            color: #333;
        }
    }

    .recording-timer {
        font-size: 24px;
        font-weight: bold;
        color: #333;
        margin-bottom: 10px;
    }

    .recording-tip {
        font-size: 14px;
        color: #999;
    }
}

/* 响应式调整 */
@media (max-width: 480px) {
    .chat-container {
        .sent {
            margin-right: 40px;

            .img {
                right: -40px;
            }
        }

        .received {
            margin-left: 40px;

            .img {
                left: -40px;
            }
        }
    }
}
</style>